package spring.cloud.tasks.api.controller;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import spring.cloud.tasks.api.domain.RestApiJobInfo;
import spring.cloud.tasks.api.domain.TaskConfiguration;
import spring.cloud.tasks.api.domain.TaskType;
import spring.cloud.tasks.api.service.NameSpaceService;
import spring.cloud.tasks.api.service.RestApiService;
import spring.cloud.tasks.api.service.TaskService;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

/**
 * RESTful APIs of Job Operations.
 */
@RequestMapping("/rest/v1")
public class TaskOperationController {

    @Resource
    private RestApiService restApiService;

    @Resource
    private NameSpaceService nameSpaceService;

    @Resource
    private TaskService taskService;

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/{namespace}/jobs", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> create(@PathVariable("namespace") String namespace,
                                         @RequestBody Map<String, Object> reqParams) throws RuntimeException {
        try {
            TaskConfiguration taskConfiguration = constructJobConfigOfCreate(namespace, reqParams);
            restApiService.createJob(namespace, taskConfiguration);
            return new ResponseEntity<>(HttpStatus.CREATED);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/{namespace}/jobs/{jobName}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> query(@PathVariable("namespace") String namespace,
                                        @PathVariable("jobName") String jobName) throws RuntimeException {
        HttpHeaders httpHeaders = new HttpHeaders();
        try {
            RestApiJobInfo restAPIJobInfo = restApiService.getRestAPIJobInfo(namespace, jobName);
            return new ResponseEntity<Object>(restAPIJobInfo, httpHeaders, HttpStatus.OK);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }


    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/{namespace}/jobs", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> queryAll(@PathVariable("namespace") String namespace)
            throws RuntimeException {
        HttpHeaders httpHeaders = new HttpHeaders();
        try {
            List<RestApiJobInfo> restApiJobInfos = restApiService.getRestApiJobInfos(namespace);
            return new ResponseEntity<Object>(restApiJobInfos, httpHeaders, HttpStatus.OK);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/{namespace}/allJobs", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> queryAllJobs(@PathVariable("namespace") String namespace)
            throws RuntimeException {
        HttpHeaders httpHeaders = new HttpHeaders();
        try {
            List<TaskConfiguration> taskConfigurationList = restApiService.getJobConfigList(namespace);
            return new ResponseEntity<Object>(taskConfigurationList, httpHeaders, HttpStatus.OK);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = {"/{namespace}/{jobName}/enable",
            "/{namespace}/jobs/{jobName}/enable"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> enable(@PathVariable("namespace") String namespace,
                                         @PathVariable("jobName") String jobName) throws RuntimeException {
        HttpHeaders httpHeaders = new HttpHeaders();
        try {
            restApiService.enableJob(namespace, jobName);
            return new ResponseEntity<>(httpHeaders, HttpStatus.OK);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = {"/{namespace}/{jobName}/disable",
            "/{namespace}/jobs/{jobName}/disable"}, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> disable(@PathVariable("namespace") String namespace,
                                          @PathVariable("jobName") String jobName) throws RuntimeException {
        HttpHeaders httpHeaders = new HttpHeaders();
        try {
            restApiService.disableJob(namespace, jobName);
            return new ResponseEntity<>(httpHeaders, HttpStatus.OK);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = {"/{namespace}/{jobName}/cron",
            "/{namespace}/jobs/{jobName}/cron"}, method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> updateJobCron(@PathVariable("namespace") String namespace,
                                                @PathVariable("jobName") String jobName, @RequestBody Map<String, String> params,
                                                HttpServletRequest request) throws RuntimeException {
        HttpHeaders httpHeaders = new HttpHeaders();
        try {
            String cron = params.remove("cron");

            restApiService.updateJobCron(namespace, jobName, cron, params);
            return new ResponseEntity<>(httpHeaders, HttpStatus.OK);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/{namespace}/jobs/{jobName}/run", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> run(@PathVariable("namespace") String namespace,
                                      @PathVariable("jobName") String jobName) throws RuntimeException {
        try {
            restApiService.runJobAtOnce(namespace, jobName, null);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/{namespace}/jobs/{jobName}/stop", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> stop(@PathVariable("namespace") String namespace,
                                       @PathVariable("jobName") String jobName) throws RuntimeException {
        try {
            restApiService.stopJobAtOnce(namespace, jobName);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/{namespace}/jobs/{jobName}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> delete(@PathVariable("namespace") String namespace,
                                         @PathVariable("jobName") String jobName) throws RuntimeException {
        try {
            restApiService.deleteJob(namespace, jobName);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/{namespace}/jobs/{jobName}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> update(@PathVariable("namespace") String namespace, @PathVariable String jobName,
                                         @RequestBody Map<String, Object> reqParams) throws RuntimeException {
        try {
            TaskConfiguration taskConfiguration = constructJobConfigOfUpdate(namespace, jobName, reqParams);
            restApiService.updateJob(namespace, jobName, taskConfiguration);
            return new ResponseEntity<>(HttpStatus.OK);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }


    //@Audit(type = AuditType.REST)
    @RequestMapping(value = "/namespaces/importJobFromNamespaceToNamespace", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResponseEntity<Object> importJobsFromNamespaceToNamespace(@RequestParam("srcNamespace") String srcNamespace,
                                                                     @RequestParam("destNamespace") String destNamespace, @RequestParam("createBy") String createBy)
            throws RuntimeException {
        try {
            Map<String, List> result = nameSpaceService.importTasksFromNameSpaceToNameSpace(srcNamespace, destNamespace, createBy);
            return new ResponseEntity<Object>(result, HttpStatus.OK);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    private TaskConfiguration constructJobConfigOfCreate(String namespace, Map<String, Object> reqParams)
            throws RuntimeException {

        if (!reqParams.containsKey("jobConfig")) {
            throw new RuntimeException(String.format("jobConfig", "cannot be blank"));
        }

        TaskConfiguration taskConfiguration = new TaskConfiguration();
        Map<String, Object> configParams = (Map<String, Object>) reqParams.get("jobConfig");

        taskConfiguration.setTaskId((String) reqParams.get("taskId"));

        taskConfiguration.setDescription((String) reqParams.get("description"));


        taskConfiguration.setCronExpression((String) reqParams.get("cron"));

        taskConfiguration.setTaskClass((String) reqParams.get("jobClass"));

        taskConfiguration.setTaskParameter((String) reqParams.get("jobParameter"));

        String jobType = (String) reqParams.get("jobType");
        if (TaskType.UNKNOWN_JOB == TaskType.getJobType(jobType)) {
            throw new RuntimeException(String.format("jobType", "is malformed"));
        }
        taskConfiguration.setTaskType(jobType);

        taskConfiguration.setLoadLevel((Integer) reqParams.get("loadLevel"));


        taskConfiguration.setPauseDatePeriod((String) reqParams.get("pausePeriodDate"));

        taskConfiguration.setPauseTimePeriod((String) reqParams.get("pausePeriodTime"));

        taskConfiguration.setPreferList((String) reqParams.get("preferList"));


        taskConfiguration.setTaskItemParameters((String) reqParams.get("shardingItemParameters"));

        taskConfiguration.setTaskItemTotalCount((Integer) reqParams.get("shardingTotalCount"));

        taskConfiguration.setTimeout4AlarmSeconds((Integer) reqParams.get("timeout4AlarmSeconds"));

        taskConfiguration.setUseDisPreferList((Boolean) reqParams.get("useDispreferList"));


        taskConfiguration.setTimeoutSeconds((Integer) reqParams.get("timeoutSeconds"));

        taskConfiguration.setProcessCountIntervalSeconds((Integer) reqParams.get("processCountIntervalSeconds"));


        taskConfiguration.setShowNormalLog((Boolean) reqParams.get("showNormalLog"));

        taskConfiguration.setFailover((Boolean) reqParams.get("failover"));

        taskConfiguration.setRerun((Boolean) reqParams.get("rerun"));


        return taskConfiguration;
    }

    private TaskConfiguration constructJobConfigOfUpdate(String namespace, String jobName, Map<String, Object> reqParams)
            throws RuntimeException {

        if (!reqParams.containsKey("jobConfig")) {
            throw new RuntimeException(String.format("jobConfig", "cannot be blank"));
        }

        TaskConfiguration taskConfiguration = new TaskConfiguration();
        Map<String, Object> configParams = (Map<String, Object>) reqParams.get("jobConfig");

        taskConfiguration.setTaskId(jobName);

        taskConfiguration.setDescription((String) reqParams.get("description"));


        taskConfiguration.setCronExpression((String) reqParams.get("cron"));

        taskConfiguration.setTaskParameter((String) reqParams.get("jobParameter"));

        taskConfiguration.setLoadLevel((Integer) reqParams.get("loadLevel"));


        taskConfiguration.setPauseDatePeriod((String) reqParams.get("pausePeriodDate"));

        taskConfiguration.setPauseTimePeriod((String) reqParams.get("pausePeriodTime"));

        taskConfiguration.setPreferList((String) reqParams.get("preferList"));


        taskConfiguration.setTaskItemParameters((String) reqParams.get("shardingItemParameters"));

        taskConfiguration.setTaskItemTotalCount((Integer) reqParams.get("shardingTotalCount"));

        taskConfiguration.setTimeout4AlarmSeconds((Integer) reqParams.get("timeout4AlarmSeconds"));

        taskConfiguration.setUseDisPreferList((boolean) reqParams.get("useDispreferList"));


        taskConfiguration.setTimeoutSeconds((Integer) reqParams.get("timeoutSeconds"));

        taskConfiguration.setProcessCountIntervalSeconds((Integer) reqParams.get("processCountIntervalSeconds"));


        taskConfiguration.setShowNormalLog((boolean) reqParams.get("showNormalLog"));

        taskConfiguration.setRerun((boolean) reqParams.get("rerun"));

        taskConfiguration.setFailover((boolean) reqParams.get("failover"));


        return taskConfiguration;
    }

}
